package com.bayi.storage.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Created by guoping on 15/12/12.
 */
public class ClassUtils {

    private static final Class[] baseClasses = new Class[] {
            char.class, byte.class, short.class, int.class, long.class, float.class, double.class, boolean.class
    };

    private static final Class[] baseClasses1 = new Class[] {
            Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class
    };

    public static Object getValue(Object obj, final String path) {
        String[] paths = path.split("\\.");
        for (String seg : paths) {
            obj = getChild(obj, seg);
            if (obj == null) {
                break;
            }
        }
        return obj;
    }

    public static Object getChild(Object object, String fieldName) {
        try {
            Class clazz = object.getClass();
            Method method = clazz.getDeclaredMethod("get" + getUpperFieldName(fieldName));
            if (method != null) {
                method.setAccessible(true);
                return method.invoke(object);
            }
            Field f = getField(clazz, fieldName);
            if (f != null) {
                f.setAccessible(true);
                return f.get(object);
            }
            return null;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    public static boolean isSubClass(Class subClass, Class parentClass) {
        if (subClass == null || parentClass == null) {
            return false;
        }

        if (subClass.equals(parentClass)) {
            return true;
        }

        if (isSubClass(subClass.getSuperclass(), parentClass)) {
            return true;
        }

        Class[] interfaces = subClass.getInterfaces();
        for (Class i:interfaces) {
            if (isSubClass(i, parentClass)) {
                return true;
            }
        }

        return false;
    }

    public static boolean setFieldValue(Object obj, String field, Object value) {
        try {
            Field f = getField(obj.getClass(), field);
            if (f == null) {
                return false;
            }
            f.setAccessible(true);
            f.set(obj, value);
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    public static <T> T getFieldValue(Object obj, String field) {
        try {
            Field f = getField(obj.getClass(), field);
            if (f == null) {
                return null;
            }
            f.setAccessible(true);
            return (T)f.get(obj);
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    public static Field getField(Class clazz, String fieldName) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.getName().equals(fieldName)) {
                return field;
            }
        }
        if (clazz.getSuperclass() != null) {
            return getField(clazz.getSuperclass(), fieldName);
        }
        return null;
    }

    public static boolean isBaseType(Class clazz) {
        return indexOf(baseClasses, clazz) != -1 || indexOf(baseClasses1, clazz) != -1;
    }

    public static Object getDefaultBaseTypeValue(Class clazz) {
        if (clazz == char.class || clazz == Character.class) {
            return (char)0;
        } else if (clazz == byte.class || clazz == Byte.class) {
            return (byte)0;
        } else if (clazz == short.class || clazz == Short.class) {
            return (short)0;
        } else if (clazz == int.class || clazz == Integer.class) {
            return 0;
        } else if (clazz == long.class || clazz == Long.class) {
            return 0L;
        } else if (clazz == float.class || clazz == Float.class) {
            return 0.F;
        } else if (clazz == double.class || clazz == Double.class) {
            return 0.;
        } else if (clazz == boolean.class || clazz == Boolean.class) {
            return false;
        } else {
            return null;
        }
    }

    public static Object parseBaseTypeValue(Class clazz, String data) {
        if (clazz == char.class || clazz == Character.class) {
            return (char)(int)Integer.parseInt(data);
        } else if (clazz == byte.class || clazz == Byte.class) {
            return Byte.parseByte(data);
        } else if (clazz == short.class || clazz == Short.class) {
            return Short.parseShort(data);
        } else if (clazz == int.class || clazz == Integer.class) {
            return Integer.parseInt(data);
        } else if (clazz == long.class || clazz == Long.class) {
            return Long.parseLong(data);
        } else if (clazz == float.class || clazz == Float.class) {
            return Float.parseFloat(data);
        } else if (clazz == double.class || clazz == Double.class) {
            return Double.parseDouble(data);
        } else if (clazz == boolean.class || clazz == Boolean.class) {
            return Boolean.parseBoolean(data);
        } else {
            return null;
        }
    }

    public static Class getSameBaseType(Class baseType) {
        int index = indexOf(baseClasses, baseType);
        if (index !=-1){
            return baseClasses1[index];
        }
        index = indexOf(baseClasses1, baseType);
        if (index !=-1){
            return baseClasses[index];
        }
        return null;
    }

    public static ParameterizedType parameterizedType(final Class raw, final Type... args) {
        return new ParameterizedType() {
            public Type getRawType() {
                return raw;
            }

            public Type[] getActualTypeArguments() {
                return args;
            }

            public Type getOwnerType() {
                return null;
            }
        };
    }

    public static String getUpperFieldName(String fieldName) {
        return new String(new char[]{fieldName.charAt(0)}).toUpperCase() + fieldName.substring(1);
    }

    public static String getLowerFieldName(String fieldName) {
        return new String(new char[]{fieldName.charAt(0)}).toLowerCase() + fieldName.substring(1);
    }

    private static int indexOf(Class[] classes, Class clazz) {
        int index = 0;
        for (Class c : classes) {
            if (c == clazz) {
                return index;
            }
            ++index;
        }
        return -1;
    }
}
